﻿using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using NuGet.Configuration;
using NuGet.Protocol;
using NuGet.Protocol.Core.Types;
using QuickGet.Core.Models;
using System.Collections.Concurrent;

namespace QuickGet.Core
{
    public class NugetResources
    {
        protected virtual IOptionsMonitor<QuickGetOptions> OptionsMonitor { get; set; }
        protected virtual ILogger Logger { get; set; }

        public NugetResources(IOptionsMonitor<QuickGetOptions> optionsMonitor, ILogger<NugetResources> logger)
        {
            OptionsMonitor = optionsMonitor;
            Repos = new ConcurrentDictionary<string, SourceRepository>();
            Logger = logger;
            PackageSources = new ConcurrentDictionary<string, PackageSource>();
        }

        protected virtual ConcurrentDictionary<string, SourceRepository> Repos { get; set; }
        protected virtual ConcurrentDictionary<string, PackageSource> PackageSources { get; set; }

        public SourceRepository? GetRepositoryAsync(string sourceName)
        {
            if (Repos.TryGetValue(sourceName, out var repository))
            {
                return repository;
            }

            if (OptionsMonitor.CurrentValue.SourceList.TryGetValue(sourceName, out var url))
            {
                var repo = Repository.Factory.GetCoreV3(url);

                Repos.TryAdd(sourceName, repo);

                return repo;
            }

            Logger.LogDebug("PackageSource: '{name}' not found", sourceName);

            return null;
        }

        public async Task<FindPackageByIdResource?> GetFindPackageByIdResourceAsync(string name, CancellationToken cancellationToken)
        {
            var repo = GetRepositoryAsync(name);

            if (repo is null)
            {
                return null;
            }

            var resource = await repo.GetResourceAsync<FindPackageByIdResource>();
            return resource;
        }

        public async Task<PackageSearchResource?> GetSearchResourceAsync(string name, CancellationToken cancellationToken)
        {
            var repo = GetRepositoryAsync(name);

            if (repo is null)
            {
                return null;
            }

            return await repo.GetResourceAsync<PackageSearchResource>(cancellationToken);
        }

        public async Task<FindPackageByIdResource?> GetFindPackageByIdResourceAsync(PackageSourceInfo packageSource, CancellationToken cancellationToken)
        {
            var repo = Repos.GetOrAdd(packageSource.Name, key => Repository.Factory.GetCoreV3(packageSource.Source));

            return await repo.GetResourceAsync<FindPackageByIdResource>(cancellationToken);
        }

        public async Task<PackageSearchResource?> GetSearchResourceAsync(PackageSourceInfo packageSource, CancellationToken cancellationToken)
        {
            var repo = Repos.GetOrAdd(packageSource.Name, key => Repository.Factory.GetCoreV3(packageSource.Source));

            return await repo.GetResourceAsync<PackageSearchResource>(cancellationToken);
        }
    }
}
